#include <array>
#include "rtmp_handshake.hpp"

/*
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|   version     |
+-+-+-+-+-+-+-+-+
C0 and S0 bits

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random bytes |
| (cont)       |
| ....         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C1 and S1 bits

Time (4 bytes): This field contains a timestamp, which SHOULD be
used as the epoch for all future chunks sent from this endpoint.
This may be 0, or some arbitrary value. To synchronize multiple
chunkstreams, the endpoint may wish to send the current value of
the other chunkstream’s timestamp.
Zero (4 bytes): This field MUST be all 0s.
Random data (1528 bytes): This field can contain any arbitrary
values. Since each endpoint has to distinguish between the
response to the handshake it has initiated and the handshake
initiated by its peer,this data SHOULD send something sufficiently
random. But there is no need for cryptographically-secure
randomness, or even dynamic values


0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| time2 (4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random echo |
| (cont) |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
C2 and S2 bits

Time (4 bytes): This field MUST contain the timestamp sent by the
peer in S1 (for C2) or C1 (for S2).
Time2 (4 bytes): This field MUST contain the timestamp at which the
previous packet(s1 or c1) sent by the peer was read.
Random echo (1528 bytes): This field MUST contain the random data
field sent by the peer in S1 (for C2) or S2 (for C1). Either peer
can use the time and time2 fields together with the current
timestamp as a quick estimate of the bandwidth and/or latency of
the connection, but this is unlikely to be useful



 * */

#include <array>
#include "spdlog/spdlog.h"

#include "rtmp_handshake.hpp"
using namespace mms;

RtmpHandshake::RtmpHandshake(std::shared_ptr<SocketInterface> conn) : conn_(conn)
{
}

RtmpHandshake::~RtmpHandshake()
{
}

boost::asio::awaitable<bool> RtmpHandshake::do_server_handshake()
{
    std::array<uint8_t, 1537> c0c1;
    if (!(co_await conn_->recv(c0c1.data(), 1537, 4000)))
    {
        co_return false;
    }

    std::array<uint8_t, 3073> s0s1s2;
    _genS0S1S2(c0c1.data(), s0s1s2.data());
    // send s0, s1, s2
    if (!(co_await conn_->send(s0s1s2.data(), 3073, 4000)))
    {
        co_return false;
    }

    std::array<uint8_t, 1536> c2;
    if (!(co_await conn_->recv(c2.data(), 1536, 4000)))
    {
        co_return false;
    }

    co_return true;
}

boost::asio::awaitable<bool> RtmpHandshake::do_client_handshake()
{
    uint8_t c0c1[1537];
    c0c1[0] = 0x03;
    *(uint32_t *)(c0c1 + 1) = time(NULL);
    *(uint32_t *)(c0c1 + 5) = 0;
    if (!(co_await conn_->send(c0c1, 1537, 4000)))
    {
        co_return false;
    }

    uint8_t s0s1s2[3073];
    // send s0, s1, s2
    if (!(co_await conn_->recv(s0s1s2, 3073, 4000)))
    {
        co_return false;
    }

    uint8_t c2[1536];
    memcpy(c2, s0s1s2, 4);
    memcpy(c2 + 4, c0c1 + 1, 4);
    memcpy(c2 + 8, s0s1s2 + 1537 + 8, 1528);
    if (!(co_await conn_->send(c2, 1536, 4000)))
    {
        co_return false;
    }

    co_return true;
}

void RtmpHandshake::_genS0S1S2(uint8_t *c0c1, uint8_t *s0s1s2)
{
    // s0
    s0s1s2[0] = '\x03';
    // s1
    memset(s0s1s2 + 1, 0, 8);
    // s2
    int32_t t = ntohl(*(int32_t *)(c0c1 + 1));
    *(int32_t *)(s0s1s2 + 1537) = htonl(t);
    *(int32_t *)(s0s1s2 + 1541) = 0; // htonl(time(NULL));
    memcpy(s0s1s2 + 1545, c0c1 + 9, 1528);
}